﻿using System;
using System.Collections.Generic;
using System.Windows;
using GameEngine.Libs;
using GameEngine.Model.Data;
using System.Xml.Linq;

namespace GameEngine.Model
{
    public class GameDataProxy<IdType> : DataProxy, IGameDataProxy<IdType>
        where IdType : IComparable<IdType>
    {
        public string TILESIZEUPDATED_NOTIFICATION
        {
            get;
            protected set;
        }
        public string MAXHEIGHTUPDATED_NOTIFICATION
        {
            get;
            protected set;
        }
        public string MINHEIGHTUPDATED_NOTIFICATION
        {
            get;
            protected set;
        }
        public string TILEHEIGHTUNITUPDATED_NOTIFICATION
        {
            get;
            protected set;
        }
        public string LEVELCHANGE_NOTIFICATION { get; protected set; }

        public string LEVELADDED_NOTIFICATION { get; protected set; }
        public string LEVELREMOVED_NOTIFICATION { get; protected set; }
        public string THINGTYPEADDED_NOTIFICATION { get;protected set; }
        public string THINGTYPEREMOVED_NOTIFICATION { get; protected set; }
        public string RESOURCEADDED_NOTIFICATION { get; protected set; }
        public string RESOURCEREMOVED_NOTIFICATION { get; protected set; }
        public string VIEWADDED_NOTIFICATION { get; protected set; }
        public string VIEWREMOVED_NOTIFICATION { get; protected set; }
        public string VIEWSTATEADDED_NOTIFICATION { get; protected set; }
        public string VIEWSTATEREMOVED_NOTIFICATION { get; protected set; }

        public IGameVOId Id
        {
            get { return VO.Id; }
        }
        public string Name {
            get { return VO.Name; }
            set { VO.Name = value; }
        }
        public Point TileSize
        {
            get { return VO.TileSize; }
            set { VO.TileSize = value; }
        }
        public float MaxHeight
        {
            get { return VO.MaxHeight; }
            set { VO.MaxHeight = value; }
        }
        public float MinHeight
        {
            get { return VO.MinHeight; }
            set { VO.MinHeight = value; }
        }
        public float TileHeightUnit
        {
            get { return VO.TileHeightUnit; }
            set { VO.TileHeightUnit = value; }
        }

        public List<IViewStateVO> ViewStates{  get{ return VO.ViewStates;}}
        IDictionary<IThingTypeVOId, IThingTypeVO<IdType>> thingTypes;
        public IDictionary<IThingTypeVOId, IThingTypeVO<IdType>> ThingTypes
        {
            get
            {
                if (thingTypes == null)
                {
                    thingTypes = new Dictionary<IThingTypeVOId, IThingTypeVO<IdType>>();
                    foreach (var lvl in VO.ThingTypes)
                        thingTypes.Add(lvl.Id, lvl);
                }
                return thingTypes;
            }
        }
        IDictionary<IViewVOId<IdType>, IViewVO<IdType>> views;
        public IDictionary<IViewVOId<IdType>, IViewVO<IdType>> Views
        {
            get
            {
                if (views == null)
                {
                    views = new Dictionary<IViewVOId<IdType>, IViewVO<IdType>>();
                    foreach (var lvl in VO.Views)
                        views.Add(lvl.Id, lvl);
                }
                return views;
            }
        }
        IDictionary<ILevelVOId, ILevelVO<IdType>> levels;
        public IDictionary<ILevelVOId, ILevelVO<IdType>> Levels { get {
            if (levels == null) {
                levels = new Dictionary<ILevelVOId, ILevelVO<IdType>>();
                foreach (var lvl in VO.Levels)
                    levels.Add(lvl.Id, lvl);
            }                
            return levels; } }
        IDictionary<IResourceVOId, IResourceVO> resources;
        public IDictionary<IResourceVOId, IResourceVO> Resources
        {
            get
            {
                if (resources == null)
                {
                    resources = new Dictionary<IResourceVOId, IResourceVO>();
                    foreach (var lvl in VO.Resources)
                        resources.Add(lvl.Id, lvl);
                }
                return resources;
            }
        }

        public ILevelDataProxy<IdType> CurrentLevel
        {
            get
            {
                return ApplicationFacade.Instance.RetrieveProxy(ILevelDataProxyConstants<IdType>.CreateDataProxyName(VO.LevelOrder[VO.CurrentLevel])) as ILevelDataProxy<IdType>;
            }
            set {
                foreach(var level in VO.LevelOrder){
                    if(level.Value == value.VO.Id)
                        VO.CurrentLevel = level.Key;
                }
                SendNotification(LEVELCHANGE_NOTIFICATION);
            }
        }

        public ILevelDataProxy<IdType> NextLevel
        {
            get
            {
                //TODO mudar isto para usar o levelorder
                int nextLevel = VO.CurrentLevel + 1;
                if (VO.LevelOrder.ContainsKey(nextLevel))
                    return ApplicationFacade.Instance.RetrieveProxy(ILevelDataProxyConstants<IdType>.CreateDataProxyName(VO.LevelOrder[VO.CurrentLevel + 1])) as ILevelDataProxy<IdType>;
                else
                    return null;
            }
        }

        public IGameVO<IdType> VO
        {
            get { return Data as IGameVO<IdType>; }
        }

        public GameDataProxy(IGameVO<IdType> game)
            : base(IGameDataProxyConstants<IdType>.CreateDataProxyName(game), game)
        {
            TILESIZEUPDATED_NOTIFICATION = "tileSizeUpdated_" + ProxyName;
            LEVELCHANGE_NOTIFICATION = "LEVELCHANGE_NOTIFICATION_" + ProxyName;
            MAXHEIGHTUPDATED_NOTIFICATION = "maxHeightUpdated_" + ProxyName;
            MINHEIGHTUPDATED_NOTIFICATION = "minHeightUpdated_" + ProxyName;
            TILEHEIGHTUNITUPDATED_NOTIFICATION = "tileHeightUnitUpdated_" + ProxyName;

            LEVELADDED_NOTIFICATION = "LEVELADDED_NOTIFICATION" + ProxyName;
            LEVELREMOVED_NOTIFICATION = "LEVELREMOVED_NOTIFICATION" + ProxyName;
            THINGTYPEADDED_NOTIFICATION = "THINGTYPEADDED_NOTIFICATION" + ProxyName;
            THINGTYPEREMOVED_NOTIFICATION = "THINGTYPEREMOVED_NOTIFICATION" + ProxyName;
            RESOURCEADDED_NOTIFICATION = "RESOURCEADDED_NOTIFICATION" + ProxyName;
            RESOURCEREMOVED_NOTIFICATION = "RESOURCEREMOVED_NOTIFICATION" + ProxyName;
            VIEWADDED_NOTIFICATION = "VIEWADDED_NOTIFICATION" + ProxyName;
            VIEWREMOVED_NOTIFICATION = "VIEWREMOVED_NOTIFICATION" + ProxyName;
            VIEWSTATEADDED_NOTIFICATION = "VIEWSTATEADDED_NOTIFICATION" + ProxyName;
            VIEWSTATEREMOVED_NOTIFICATION = "VIEWSTATEREMOVED_NOTIFICATION" + ProxyName;
        }

        IDictionary<IThingTypeVOId, IThingTypeDataProxy<IdType>> itemDataProxys = new Dictionary<IThingTypeVOId, IThingTypeDataProxy<IdType>>();
        public IThingTypeDataProxy<IdType> RetrieveThingTypeDataProxy(IThingTypeVO<IdType> vo) { 
            if(itemDataProxys.Keys.Contains(vo.Id)){
                return itemDataProxys[vo.Id];
            }else{
                IThingTypeDataProxy<IdType> dataProxy = ApplicationFacade.Instance.RetrieveProxy(IThingTypeDataProxyConstants<IdType>.CreateDataProxyName(vo)) as IThingTypeDataProxy<IdType>;
                itemDataProxys.Add(vo.Id, dataProxy);
                return dataProxy;
            }
        }

        public void AddLevel(ILevelDataProxy<IdType> thingType)
        {
            Levels.Add(thingType.VO.Id, thingType.VO);
            VO.Levels.Add(thingType.VO);
            SendNotification(LEVELADDED_NOTIFICATION, thingType);
        }
        public void RemoveLevel(ILevelVOId thingType)
        {
            Levels.Remove(thingType);
            foreach (var aux in VO.Levels)
                if (aux.Id.Equals(thingType))
                    VO.Levels.Remove(aux);
            SendNotification(LEVELREMOVED_NOTIFICATION, thingType);
        }
        public void AddThingType(IThingTypeDataProxy<IdType> thingType){
            ThingTypes.Add(thingType.VO.Id, thingType.VO);
            VO.ThingTypes.Add(thingType.VO);
            SendNotification(THINGTYPEADDED_NOTIFICATION, thingType);
        }
        public void RemoveThingType(IThingTypeVOId thingType)
        {
            ThingTypes.Remove(thingType);
            foreach(var aux in VO.ThingTypes)
                if(aux.Id.Equals(thingType))
                    VO.ThingTypes.Remove(aux);
            SendNotification(THINGTYPEREMOVED_NOTIFICATION, thingType);
        }
        public void AddResource(IResourceDataProxy thingType)
        {
            Resources.Add(thingType.VO.Id, thingType.VO);
            VO.Resources.Add(thingType.VO);
            SendNotification(RESOURCEADDED_NOTIFICATION, thingType);
        }
        public void RemoveResource(ResourceVOId thingType)
        {
            Resources.Remove(thingType);
            foreach (var aux in VO.Resources)
                if (aux.Id.Equals(thingType))
                    VO.Resources.Remove(aux);
            SendNotification(RESOURCEREMOVED_NOTIFICATION, thingType);
        }
        public void AddView(IViewDataProxy<IdType> thingType)
        {
            Views.Add(thingType.VO.Id, thingType.VO);
            VO.Views.Add(thingType.VO);
            SendNotification(VIEWADDED_NOTIFICATION, thingType);
        }
        public void RemoveView(IViewVOId<IdType> thingType)
        {
            Views.Remove(thingType);
            foreach (var aux in VO.Views)
                if (aux.Id.Equals(thingType))
                    VO.Views.Remove(aux);
            SendNotification(VIEWREMOVED_NOTIFICATION, thingType);
        }
        public void AddViewState(ViewStateVO thingType)
        {
            ViewStates.Add(thingType);
            VO.ViewStates.Add(thingType);
            SendNotification(VIEWSTATEADDED_NOTIFICATION, thingType);
        }
        public void RemoveViewState(ViewStateVO thingType)
        {
            ViewStates.Remove(thingType);
            VO.ViewStates.Remove(thingType);
            SendNotification(VIEWSTATEREMOVED_NOTIFICATION, thingType);
        }
        //public virtual void LoadFromXML(XDocument doc)
        //{
        //    /*TODO List<List<ITileVO>> tiles = new List<List<ITileVO>>();
        //    foreach (XElement row in doc.Elements("tiles").Nodes())
        //    {
        //        List<ITileVO> tileRow = new List<ITileVO>();
        //        foreach (XElement tile in row.Nodes())
        //        {
        //            ITileVO tileVO = new MyTileVO();
        //            tileVO.Height = float.Parse(tile.Attribute("height").Value);
        //            foreach (XElement node in tile.Nodes())
        //                switch (node.Name.ToString())
        //                {
        //                    case "position":
        //                        tileVO.Position = new Point(
        //                            int.Parse(node.Attribute("x").Value),
        //                            int.Parse(node.Attribute("y").Value));
        //                        break;
        //                }
        //            tileRow.Add(tileVO);
        //        }
        //        tiles.Add(tileRow);
        //    }
        //    Map.Tiles = tiles;
        //    SendNotification(TILESUPDATED_NOTIFICATION, this);*/
        //}

        //public virtual string SaveToXML()
        //{
        //    return null;
        //    /*TODO
        //    XDocument doc = new XDocument();
        //    XElement mapNode = new XElement("map");
        //    XElement maxHeight = new XElement("maxHeight") { Value = Map.MaxHeight.ToString() };
        //    mapNode.Add(maxHeight);
        //    XElement size = new XElement("size");
        //    size.SetAttributeValue("x", Map.Size.X);
        //    size.SetAttributeValue("y", Map.Size.Y);
        //    mapNode.Add(size);
        //    XElement tileSize = new XElement("tileSize");
        //    tileSize.SetAttributeValue("x", Map.TileSize.X);
        //    tileSize.SetAttributeValue("y", Map.TileSize.Y);
        //    mapNode.Add(tileSize);
        //    //todo Camera
        //    XElement tilesNode = new XElement("tiles");
        //    foreach (List<ITileVO> tiles in Map.Tiles)
        //    {
        //        XElement rowNode = new XElement("row");
        //        foreach (ITileVO tile in tiles)
        //        {
        //            XElement tileNode = new XElement("tile");
        //            tileNode.SetAttributeValue("height", tile.Height.ToString());
        //            XElement position = new XElement("position");
        //            position.SetAttributeValue("x", tile.Position.X);
        //            position.SetAttributeValue("y", tile.Position.Y);
        //            tileNode.Add(position);
        //            rowNode.Add(tileNode);
        //        }
        //        tilesNode.Add(rowNode);
        //    }
        //    mapNode.Add(tilesNode);
        //    doc.Add(mapNode);
        //    return doc;*/
        //}
    }
}
